博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用string.Format需要注意的一个性能问题
阅读量:6829 次
发布时间:2019-06-26

本文共 4482 字,大约阅读时间需要 14 分钟。

今天,我在写C#代码时,突然发现一个最熟悉的陌生人 —— string.Format。在写C#代码的日子里,与它朝夕相伴,却没有真正去了解它。只知道在字符串比较多时,用它比用加号进行字符串连接效率更高(当然也更方便)。可是却从来没有问过为什么?

在生活中也有类似的现象,与你朝夕相处、你最熟悉的人,你往往不会进一步去了解她(他),你已经习惯了她(他),你认为你已经太了解她(他)了。。。真的是这样吗?这值得去思考。。。

博问中的一个问题 —— 激发了我的好奇心,想一探string.Format的究竟,而且在开发中也正好遇到一个字符串连接的问题。

了解.NET世界中的东西其实很简单,只要通过工具反编译出相应的.NET类库代码,我们来看看string.Fomat的代码:

public static string Format(string format, object arg0, object arg1, object arg2)    {      if (format == null)        throw new ArgumentNullException("format");      return string.Format((IFormatProvider) null, format, arg0, arg1, arg2);    }

实际调用的是另外一个签名的string.Format:

public static string Format(IFormatProvider provider, string format, params object[] args)    {      if (format == null || args == null)        throw new ArgumentNullException(format == null ? "format" : "args");      StringBuilder stringBuilder = new StringBuilder(format.Length + args.Length * 8);      stringBuilder.AppendFormat(provider, format, args);      return ((object) stringBuilder).ToString();    }

哦,原来用的就是StringBuilder(也许你早就知道了),string.Format只是StringBuilder的改装精简版。

既然是StringBuilder,它必然无法避免一个影响StringBuilder性能的问题 —— 初始化容量(capacity)的问题,string.Format是如何解决的呢?从上面的代码一眼就可以看出,初始化容量是这么计算出来的:

format.Length + args.Length * 8

从这个计算公式可以看出,假设需要format的字符串是10个,如果这10字符串累加起来的字符数不超过80,就能发挥StringBuilder的最佳性能;否则,StringBuider需要扩容,从而带来性能损失。

所以,对于大字符串,string.Format不是最佳选择。

那最佳选择是什么?还是StringBuilder,只不过要自己写代码计算初始化容量。分享一下今天我们在实际开发中使用的代码:

var bodyFormat = "{1}
"; var diggFormat = " 支持({2})";var buryFormat = " 反对({3})";var args = new string[]{ comment.ID.ToString(), comment.Body, comment.DiggCount.ToString(), comment.BuryCount.ToString() };//计算初始化容量int capacity = bodyFormat.Length + diggFormat.Length + buryFormat.Length;for (int i = 0; i < args.Length; i++){ capacity += args[i].Length;}var sb = new StringBuilder(capacity);sb.AppendFormat(bodyFormat,args); sb.AppendFormat(diggFormat,args); sb.AppendFormat(buryFormat,args);Post.Text = sb.ToString();

这里没有使用string.Format,一是因为comment.Body的字符数会很多,string.Format分配的初始化容量不够。二是因为string.Format不能分批Fomat,格式字符串只能写在一起,造成格式字符串很长,也就是bodyFormat, diggFormat, buryFormat要拼成一个字符串。

麻烦主要在参数字符串(args)的长度计算,要将每个字符串的字符数进行累加。我们采用的方法是将所有参数放在string[]类型的变量中,通过遍历数组进行计算,然后将这个string[]类型的变量直接传给StringBuilder.AppendFormat(它支持的参数类型是object[])。

小结

写这篇博文不是为让你弃用string.Format,而是让你了解它所存在的限制,在某些性能要求极高的场景下,可以考虑到这个影响因素。

更新

针对这个问题,实现了两个扩展方法。

1. 针对单个格式字符串

namespace System{    public static class StringExtension    {        public static string FormatWith(this string format, params object[] args)        {            if (format == null || args == null)            {                throw new ArgumentNullException((format == null) ? "format" : "args");            }            else            {                var capacity = format.Length + args.Where(a => a != null).Select(p => p.ToString()).Sum(p => p.Length);                Console.WriteLine(capacity);                var stringBuilder = new StringBuilder(capacity);                stringBuilder.AppendFormat(format, args);                return stringBuilder.ToString();            }        }            }}

调用示例:

"welcome to {0}! welcome to {1}!".FormatWith("www.cnblogs.com", "q.cnblogs.com");

2. 针对多个格式字符串

namespace System{    public static class StringExtension    {        public static string FormatWith(this IEnumerable
formats, params object[] args) { if (formats == null || args == null) { throw new ArgumentNullException((formats == null) ? "formats" : "args"); } else { var capacity = formats.Where(f => !string.IsNullOrEmpty(f)).Sum(f => f.Length) + args.Where(a => a != null).Select(p => p.ToString()).Sum(p => p.Length); var stringBuilder = new StringBuilder(capacity); foreach (var f in formats) { if (!string.IsNullOrEmpty(f)) { stringBuilder.AppendFormat(f, args); } } return stringBuilder.ToString(); } } }}

调用示例:

new string[] { "welcome to {0}!", " welcome to {1}!" }.FormatWith("www.cnblogs.com", "q.cnblogs.com");

前面使用StringBuilder的代码改为调用扩展方法:

Post.Text = new string[]{"{1}
"," 支持({2})"," 反对({3})"}.FormatWith(comment.ID, comment.Body, comment.DiggCount, comment.BuryCount);

转载于:https://www.cnblogs.com/dudu/archive/2012/05/29/string_format_stringbuilder.html

你可能感兴趣的文章
tcc新的插装引擎对比原有实现的改进
查看>>
20145328 《信息安全系统设计基础》第3周学习总结
查看>>
layoutSubviews何时调用的问题
查看>>
编译bash实现history的syslog日志记录
查看>>
Java数据类型
查看>>
mysql主从备份
查看>>
我的友情链接
查看>>
强化学习概览
查看>>
Oracle启动出现ORA-27125错误
查看>>
读书笔记之委托与事件(下)
查看>>
我的友情链接
查看>>
Linux grep 查找字符串 前后几行
查看>>
P3402 最长公共子序列(nlogn)
查看>>
我的友情链接
查看>>
Android基础(六) – Service属性及常用方法
查看>>
C#实现对站点、程序池状态的监控,以及URL能正常返回的监控,状态异常,邮件预警...
查看>>
看看清华的大学生都在做什么
查看>>
Spring MVC 滤器实现去除空格、增加参数功能
查看>>
androi开发 Bitmap回收问题
查看>>
常用Jquery操作总结
查看>>